# -*- coding: utf-8 -*-
# Copyright 2023 Ant Group CO., Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
# or implied.

import os
import re
import string
import sys
from configparser import ConfigParser
from pathlib import Path
from shutil import copy2, copystat
from stat import S_IWUSR as OWNER_WRITE_PERMISSION
from typing import Optional

import click
from tabulate import tabulate

from knext import rest
from knext.common.template import render_templatefile

TEMPLATES_TO_RENDER = (
    (".knext.cfg.tmpl",),
    ("README.md.tmpl",),
    ("schema", "${project}.schema.tmpl"),
    ("reasoner", "demo.dsl.tmpl"),
    ("builder", "operator", "demo_extract_op.py"),
    ("builder", "job", "data", "Demo.csv"),
    ("builder", "job", "demo.py.tmpl"),
)


def list_project():
    """
    List all project information.
    """
    client = rest.ProjectApi()
    projects = client.project_get()

    table_data = []
    for project in projects:
        item = project.to_dict()
        table_data.append(
            [item["id"], item["name"], item["namespace"], item["description"]]
        )

    table_headers = ["ID", "Name", "Namespace", "Description"]

    table = tabulate(table_data, table_headers, tablefmt="github")
    click.echo(table)


def _recover_project(prj_path: str):
    """
    Recover project by a project dir path.
    """
    if not Path(prj_path).exists():
        click.secho(f"ERROR: No such directory: {prj_path}", fg="bright_red")
        sys.exit()

    if prj_path not in sys.path:
        sys.path.append(prj_path)
    prj = Path(prj_path).resolve()

    cfg_file = prj / ".knext.cfg"
    cfg = ConfigParser()
    cfg.read(cfg_file)
    project_name = cfg.get("local", "project_name")
    namespace = cfg.get("local", "namespace")
    desc = cfg.get("local", "description")

    client = rest.ProjectApi()
    project_create_request = rest.ProjectCreateRequest(
        name=project_name, desc=desc, namespace=namespace
    )
    project = client.project_create_post(project_create_request=project_create_request)

    cfg.set("local", "project_id", str(project.id))
    with open(cfg_file, "w") as config_file:
        cfg.write(config_file)

    click.secho(
        f"Project [{project_name}] with namespace [{namespace}] was successfully created from [{prj_path}].",
        fg="bright_green",
    )


@click.option("--name", help="Name of project.")
@click.option("--namespace", help="Prefix of project schema.")
@click.option("--desc", help="Description of project.")
@click.option(
    "--prj_path",
    help="If set, project will be created according to config file of this path.",
)
def create_project(
    name: str, namespace: str, desc: Optional[str], prj_path: Optional[str]
):
    """
    Create new project with a demo case.
    """

    if prj_path:
        _recover_project(prj_path)
        sys.exit()

    if not name:
        click.secho("ERROR: Option [--name] is required.")
        sys.exit()
    if not namespace:
        click.secho("ERROR: Option [--namespace] is required.")
        sys.exit()

    if not re.match(r"^[A-Z][A-Za-z0-9]{0,15}$", namespace):
        raise click.BadParameter(
            f"Invalid namespace: {namespace}."
            f" Must start with an uppercase letter, only contain letters and numbers, and have a maximum length of 16."
        )

    project_dir = Path(namespace.lower())
    if project_dir.exists():
        raise click.ClickException(
            f"Project directory [{namespace.lower()}] already exists."
        )
    client = rest.ProjectApi()
    project_create_request = rest.ProjectCreateRequest(
        name=name, desc=desc, namespace=namespace
    )
    project = client.project_create_post(project_create_request=project_create_request)

    import knext

    templates_dir = Path(knext.__path__[0]) / "templates/project"
    _copytree(Path(templates_dir), project_dir.resolve(), namespace.lower())
    for paths in TEMPLATES_TO_RENDER:
        tplfile = Path(
            project_dir,
            *(string.Template(s).substitute(project=namespace.lower()) for s in paths),
        )
        render_templatefile(
            tplfile,
            project_name=name,
            namespace=namespace,
            project_dir=namespace.lower(),
            project_id=project.id,
            description=desc,
            helper=f"{namespace.lower()}_schema_helper",
        )

    click.secho(
        f"Project [{name}] with namespace [{namespace}] was successfully created in {project_dir.resolve()} \n"
        + "You can checkout your project with: \n"
        + f"  cd {project_dir}",
        fg="bright_green",
    )


def _copytree(src: Path, dst: Path, project_name: str):
    names = [x.name for x in src.iterdir()]

    if not dst.exists():
        dst.mkdir(parents=True)

    for name in names:
        _name = name.replace("${project}", project_name)
        src_name = src / name
        dst_name = dst / _name
        if src_name.is_dir():
            _copytree(src_name, dst_name, project_name)
        else:
            copy2(src_name, dst_name)
            _make_writable(dst_name)

    copystat(src, dst)
    _make_writable(dst)


def _make_writable(path):
    current_permissions = os.stat(path).st_mode
    os.chmod(path, current_permissions | OWNER_WRITE_PERMISSION)
